package com.ruoyi.app.service.impl

import cn.hutool.core.bean.BeanUtil
import cn.hutool.core.lang.func.Func
import com.baomidou.mybatisplus.extension.kotlin.KtQueryWrapper
import com.baomidou.mybatisplus.extension.plugins.pagination.Page
import com.ruoyi.ability.Ability
import com.ruoyi.ability.CartoonParam
import com.ruoyi.ability.PicToPicParam
import com.ruoyi.ability.TextToPicParam
import com.ruoyi.app.common.*
import com.ruoyi.app.domain.v3.dto.MyCollectDto
import com.ruoyi.app.domain.v3.dto.MyRecordDto
import com.ruoyi.app.domain.v3.vo.MyRecordVo
import com.ruoyi.app.domain.vo.FunctionVo
import com.ruoyi.app.domain.vo.PicProcessResultVo
import com.ruoyi.app.service.UserRecordApiService
import com.ruoyi.back.domain.Function
import com.ruoyi.back.domain.FunctionSon
import com.ruoyi.back.domain.v3.MyCollect
import com.ruoyi.back.domain.v3.MyRecord
import com.ruoyi.back.domain.v3.Recommend
import com.ruoyi.back.domain.v3.StyleTypeEnum
import com.ruoyi.back.service.AppUserService
import com.ruoyi.back.service.ChannelManagerService
import com.ruoyi.back.service.FunctionService
import com.ruoyi.back.service.v3.*
import com.ruoyi.exception.FlyException
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service

@Service
class UserRecordApiServiceImpl : UserRecordApiService {

    @Autowired
    private lateinit var netUrlUploadOss: NetUrlUploadOss

    @Autowired
    private lateinit var ability: Ability

    @Autowired
    private lateinit var styleSettingService: StyleSettingService

    @Autowired
    private lateinit var recommendService: RecommendService

    @Autowired
    private lateinit var myCollectService: MyCollectService

    @Autowired
    private lateinit var myRecordService: MyRecordService

    @Autowired
    private lateinit var appUserService: AppUserService

    @Autowired
    private lateinit var functionService: FunctionService

    @Autowired
    private lateinit var templateReportService: TemplateReportService

    @Autowired
    private lateinit var channelManagerService: ChannelManagerService

    @Autowired
    private lateinit var sensitiveWordRecordService: SensitiveWordRecordService

    private val log = LoggerFactory.getLogger(UserRecordApiServiceImpl::class.java)
    override fun functionResolution(type: Int): Data<List<FunctionSon>> =
        Ok("获取成功", if (type != 3) {
            functionService.list(
                KtQueryWrapper(Function::class.java)
                    .eq(Function::pid, type)
                    .orderByDesc(Function::orderNum)
            )
                .mapNotNull {
                    FunctionSon().apply {
                        BeanUtil.copyProperties(it, this)
                    }
                }
        }else {
            val result = mutableListOf<FunctionSon>()
            result.add(functionService.getById(type).let {
                FunctionSon().apply { BeanUtil.copyProperties(it, this) }
            })
            result
        })

    override fun text2pic(textToPicParam: TextToPicParam): Data<Boolean> =
        appUserService.getById(id())?.let {

            if (sensitiveWordRecordService.saveSensitiveWordRecord(SensitiveWordRecordDto(it.id, textToPicParam.text))) {
                throw FlyException("涉及到敏感词 请重新输入")
            }

            val points = (functionService.getById(textToPicParam.functionId)?.useCount ?: 0) * (textToPicParam.num ?: 1)

            if (channelManagerService.booleanToll(textToPicParam.channelCode!!)) {
                if (!textToPicParam.pay!!) {
                    if ((it.receiveCount!! - it.useCount!!) < points) {
                        return Fail(201, "可用次数不足!")
                    }
                }
                appUserService.updateById(it.apply {
                    useCount = it.useCount?.plus(points)
                })
                Ok("提交任务成功!", text2picCallback(textToPicParam))
            } else {
                Ok("提交任务成功!", text2picCallback(textToPicParam))
            }
        } ?: Fail("不存在当前用户")

    override fun text2picCallback(textToPicParam: TextToPicParam): Boolean =
        myRecordService.save(MyRecord().apply {
            appUserService.getById(id())?.let {
                userId = it.id
                username = it.username
                userInfoId = it.userId
            }
            name = styleSettingService.getNameByIds(textToPicParam.style!!)
            type = StyleTypeEnum.TEXT_PIC.type
            resultConfig = textToPicParam.resolution
            taskId = ability.textTopic(textToPicParam.apply {
                style = styleSettingService.getNameByIds(textToPicParam.style!!)
            })
                .jsonObj()
                .run {
                    if (getStr("error_msg") == null) {
                        getJSONObject("data").getStr("taskId")
                    } else {
                        throw FlyException(
                            when (getStr("error_code")) {
                                "282004" -> "请求中包含非法参数或字数超限，请检查后重新尝试"
                                "282003" -> "缺少必要参数"
                                "18" -> "QPS 超限额"
                                "216630" -> "服务器内部错误，请再次请求，如果持续出现此类错误，请通过工单联系技术支持"
                                "501" -> "文本黄反拦截"
                                "201" -> "模型生图失败"
                                "216100" -> "参数不满足格式要求"
                                "4" -> "错误信息为中文的“请求超限”指所有用户提交的 AI 作画总数超限制"
                                "13" -> "错误信息为中文的“QPS 超限”指单个用户使用提交请求接口的 QPS 超限"
                                "15" -> "错误信息为中文的“并发超限”指单个用户使用 AI 作画的并发超限"
                                "17" -> "错误信息为中文的“用量超限”指单个用户使用 AI 作画的用量超限"
                                else -> "未知错误"
                            }
                        )
                    }
                }

            status = 0
            descText = textToPicParam.text
        })


    override fun pic2pic(picToPicParam: PicToPicParam): Data<PicProcessResultVo> =

        appUserService.getById(id())?.let {

            if (sensitiveWordRecordService.saveSensitiveWordRecord(SensitiveWordRecordDto(it.id, picToPicParam.prompt))) {
                throw FlyException("涉及到敏感词 请重新输入")
            }

            val points = (functionService.getById(picToPicParam.functionId)?.useCount ?: 0)
            if (((it.receiveCount!! - it.useCount!!) < points) && !picToPicParam.pay!! && channelManagerService.booleanToll(
                    picToPicParam.channelCode!!
                )
            ) {
                return Fail(201, "可用次数不足!")
            } else {
                val myRecord = MyRecord()
                myRecordService.save(
                    myRecord.apply {
                        appUserService.getById(id())?.let {
                            userId = it.id
                            username = it.username
                            userInfoId = it.userId
                        }
                        initPic = picToPicParam.inputUrl
                        type = StyleTypeEnum.PIC_PIC.type
                        name = styleSettingService.getNameByIds(picToPicParam.styles!!)
                        descText = picToPicParam.prompt
                        resultConfig = functionService.getById(picToPicParam.functionId)?.name
                        ability.picToPic(picToPicParam).jsonObj().let {
                            if (it.getJSONObject("Response") != null) {
                                it.getJSONObject("Response").getJSONObject("Error")?.let {
                                    throw FlyException(
                                        when (it.getStr("Code")) {
                                            "FailedOperation.ConsoleServerError" -> "控制台服务异常。"
                                            "FailedOperation.GenerateImageFailed" -> "生成图片审核不通过，请重试。"
                                            "FailedOperation.ImageDecodeFailed" -> "图片解码失败。"
                                            "FailedOperation.ImageDownloadError" -> "图片下载错误。"
                                            "FailedOperation.ImageResolutionExceed" -> "图片分辨率过大，超过2000*2000。"
                                            "FailedOperation.ImageSizeExceed" -> "base64编码后的图片数据大小不超过10M。"
                                            "FailedOperation.RequestEntityTooLarge" -> "整个请求体太大（通常主要是图片）。"
                                            "FailedOperation.RequestTimeout" -> "后端服务超时。"
                                            "FailedOperation.RpcFail" -> "RPC请求失败，一般为算法微服务故障。"
                                            "FailedOperation.ServerError" -> "服务内部错误。"
                                            "FailedOperation.Unknown" -> "FailedOperation.Unknown"
                                            "InvalidParameter.InvalidParameter" -> "参数不合法。"
                                            "InvalidParameterValue.ImageEmpty" -> "图片为空。"
                                            "InvalidParameterValue.ParameterValueError" -> "参数字段或者值有误"
                                            "InvalidParameterValue.StyleConflict" -> "1xx和其他风格不可混合使用。"
                                            "InvalidParameterValue.TextLengthExceed" -> "InvalidParameterValue.TextLengthExceed"
                                            "InvalidParameterValue.UrlIllegal" -> "InvalidParameterValue.UrlIllegal"
                                            "OperationDenied.ImageIllegalDetected" -> "图片包含违法违规信息，审核不通过。"
                                            "OperationDenied.TextIllegalDetected" -> "文本包含违法违规信息，审核不通过。"
                                            "ResourceUnavailable.InArrears" -> "帐号已欠费。"

                                            "ResourceUnavailable.LowBalance" -> "余额不足。"
                                            "ResourceUnavailable.NotExist" -> "计费状态未知，请确认是否已在控制台开通服务。"
                                            "ResourceUnavailable.StopUsing" -> "帐号已停服。"
                                            "ResourcesSoldOut.ChargeStatusException" -> "计费状态异常。"
                                            else -> "未知异常"
                                        }
                                    )
                                }
                            }

                            pics = netUrlUploadOss.uploadBase64(it.getStr("ResultImage"))
                        }
                    }
                )
                if (channelManagerService.booleanToll(picToPicParam.channelCode!!)) {
                    appUserService.updateById(it.apply {
                        useCount = it.useCount?.plus(points)
                    })
                }
                return Ok("生成成功", PicProcessResultVo().apply {
                    prompt = picToPicParam.prompt
                    resultImage = myRecord.pics?.split(",")
                    recordId = myRecord.id
                })
            }
        } ?: Fail("不存在当前用户")

    override fun pic2picCallback(picToPicParam: PicToPicParam): Boolean {
        val myRecord = MyRecord()
        return myRecordService.save(
            myRecord.apply {
                appUserService.getById(picToPicParam.userId)?.let {
                    userId = it.id
                    username = it.username
                    userInfoId = it.userId
                }
                initPic = picToPicParam.inputUrl
                type = StyleTypeEnum.PIC_PIC.type
                name = styleSettingService.getNameByIds(picToPicParam.styles!!)
                descText = picToPicParam.prompt
                resultConfig = functionService.getById(picToPicParam.functionId)?.name
                ability.picToPic(picToPicParam).jsonObj().let {

                    if (it.getJSONObject("Response") != null) {
                        it.getJSONObject("Response").getJSONObject("Error")?.let {
                            throw FlyException(
                                when (it.getStr("Code")) {
                                    "FailedOperation.ConsoleServerError" -> "控制台服务异常。"
                                    "FailedOperation.GenerateImageFailed" -> "生成图片审核不通过，请重试。"
                                    "FailedOperation.ImageDecodeFailed" -> "图片解码失败。"
                                    "FailedOperation.ImageDownloadError" -> "图片下载错误。"
                                    "FailedOperation.ImageResolutionExceed" -> "图片分辨率过大，超过2000*2000。"
                                    "FailedOperation.ImageSizeExceed" -> "base64编码后的图片数据大小不超过10M。"
                                    "FailedOperation.RequestEntityTooLarge" -> "整个请求体太大（通常主要是图片）。"
                                    "FailedOperation.RequestTimeout" -> "后端服务超时。"
                                    "FailedOperation.RpcFail" -> "RPC请求失败，一般为算法微服务故障。"
                                    "FailedOperation.ServerError" -> "服务内部错误。"
                                    "FailedOperation.Unknown" -> "FailedOperation.Unknown"
                                    "InvalidParameter.InvalidParameter" -> "参数不合法。"
                                    "InvalidParameterValue.ImageEmpty" -> "图片为空。"
                                    "InvalidParameterValue.ParameterValueError" -> "参数字段或者值有误"
                                    "InvalidParameterValue.StyleConflict" -> "1xx和其他风格不可混合使用。"
                                    "InvalidParameterValue.TextLengthExceed" -> "InvalidParameterValue.TextLengthExceed"
                                    "InvalidParameterValue.UrlIllegal" -> "InvalidParameterValue.UrlIllegal"
                                    "OperationDenied.ImageIllegalDetected" -> "图片包含违法违规信息，审核不通过。"
                                    "OperationDenied.TextIllegalDetected" -> "文本包含违法违规信息，审核不通过。"
                                    "ResourceUnavailable.InArrears" -> "帐号已欠费。"

                                    "ResourceUnavailable.LowBalance" -> "余额不足。"
                                    "ResourceUnavailable.NotExist" -> "计费状态未知，请确认是否已在控制台开通服务。"
                                    "ResourceUnavailable.StopUsing" -> "帐号已停服。"
                                    "ResourcesSoldOut.ChargeStatusException" -> "计费状态异常。"
                                    else -> "未知异常"
                                }
                            )
                        }
                    }

                    pics = netUrlUploadOss.uploadBase64(it.getStr("ResultImage"))
                }
            }
        )
    }

    override fun cartoonPicCallback(cartoonParam: CartoonParam): Boolean {
        val myRecord = MyRecord()
        return myRecordService.save(myRecord.apply {
            appUserService.getById(cartoonParam.userId)?.let {
                userId = it.id
                username = it.username
                userInfoId = it.userId
            }
            type = StyleTypeEnum.KA_TONG_PIC.type
            name = styleSettingService.getNameByIds(cartoonParam.style!!)
            ability.cartoonPic(cartoonParam).jsonObj().let {
                pics = netUrlUploadOss.uploadBase64(it.getJSONObject("data").getStr("imageBase64"))
            }
        })
    }

    override fun cartoonPic(cartoonParam: CartoonParam): Data<PicProcessResultVo> =

        appUserService.getById(id())?.let {
            if (((it.receiveCount!! - it.useCount!!) < functionService.getFunctionUseCount(3)) && !cartoonParam.pay!! && channelManagerService.booleanToll(
                    cartoonParam.channelCode!!
                )
            ) {
                return Fail(201, "可用次数不足!")
            } else {
                val myRecord = MyRecord()
                myRecordService.save(myRecord.apply {
                    appUserService.getById(id())?.let {
                        userId = it.id
                        username = it.username
                        userInfoId = it.userId
                    }
                    type = StyleTypeEnum.KA_TONG_PIC.type
                    name = styleSettingService.getNameByIds(cartoonParam.style!!)
                    ability.cartoonPic(cartoonParam).jsonObj().let {

                        it.getStr("code").takeIf { it.isNotBlank() && it != "0" }?.let {
                            throw FlyException(
                                when (it) {
                                    "1001" -> "请求失败，对于未分类的错误都会采用此错误编号，并且msg字段显示具体错误信息"
                                    "1002" -> "文件大小必须小于15M"
                                    "4001" -> "你的点数已不足"
                                    "4002" -> "文件不存在"
                                    "5002" -> "非法的api key"
                                    "5003" -> "图片处理失败"
                                    "5004" -> "网络图片下载失败"
                                    "5005" -> "图片处理请求过多，请稍后"
                                    "5006" -> "非法的base64字符串"
                                    "5007" -> "base64字符串不能被正确识别为图片, 部分java库存在base64换行符问题，需要将换行符去除"
                                    "5008" -> "无法正确识别输入图片"
                                    "7001" -> "无法识别到人脸，请换一张照片"
                                    "7002" -> "检测到多人体，也许有他人或图案影响。您可以换一张照片重试。"
                                    else -> "未知错误"
                                }
                            )
                        }

                        pics = netUrlUploadOss.uploadBase64(it.getJSONObject("data").getStr("imageBase64"))
                    }
                })

                if (channelManagerService.booleanToll(cartoonParam.channelCode!!)) {
                    appUserService.updateById(it.apply {
                        useCount = it.useCount?.plus(functionService.getById(3)?.useCount ?: 0)
                    })
                }
                return Ok("生成成功!", PicProcessResultVo().apply {
                    resultImage = myRecord.pics?.split(",")
                    recordId = myRecord.id
                })
            }
        } ?: Fail("不存在当前用户")

    override fun myRecordList(myRecordDto: MyRecordDto): Data<PageData<MyRecordVo>> =
        myRecordService.page(
            Page(myRecordDto.pageNum!!, myRecordDto.pageSize!!), KtQueryWrapper(MyRecord::class.java)
                .eq(MyRecord::type, myRecordDto.type)
                .eq(MyRecord::userId, id())
                .isNull(MyRecord::pid)
                .orderByDesc(MyRecord::createTime)
        )
            .let { page ->
                Ok(
                    "获取我的作品成功", Page<MyRecordVo>()
                        .apply {
                            BeanUtil.copyProperties(page, this)
                            records = page.records.takeIf { it.isNotEmpty() }
                                ?.run {
                                    map { myrecord ->
                                        val myRecordVo = trans(myrecord)
                                        if (myrecord.type == 1) {
                                            myRecordService.list(
                                                KtQueryWrapper(MyRecord::class.java)
                                                    .eq(MyRecord::pid, myrecord.id)
                                            )
                                                .takeIf { it.isNotEmpty() }
                                                ?.run {
                                                    myRecordVo.text2PicRecord = map {
                                                        trans(it)
                                                    }
                                                    myRecordVo.pics = mapNotNull { it.pics }.joinToString(",")
                                                }
                                        }

                                        myRecordVo
                                    }
                                }
                        }.pageInfo()
                )
            }

    fun trans(myRecord: MyRecord): MyRecordVo =
        MyRecordVo().apply {
            BeanUtil.copyProperties(myRecord, this)
            like = myCollectService.like(myRecord.id!!)
            likeNum = likeNum(myRecord.id?.toInt()!!).toInt()
            createTime = myRecord.createTime?.format()
            myCollectService.getOne(
                KtQueryWrapper(MyCollect::class.java)
                    .eq(MyCollect::mode, 2)
                    .eq(MyCollect::recordId, myRecord.id)
                    .eq(MyCollect::userId, id())
            )
                ?.run {
                    collect = true
                }
            recommendService.getOne(
                KtQueryWrapper(Recommend::class.java)
                    .eq(Recommend::userId, id())
                    .eq(Recommend::recordId, myRecord.id)
                    .last("limit 1")
            )?.run {
                recommend = true
            }
        }

    override fun myCollect(myCollectDto: MyCollectDto): Data<PageData<MyRecordVo>> =
        myCollectService.page(
            Page(myCollectDto.pageNum!!, myCollectDto.pageSize!!),
            KtQueryWrapper(MyCollect::class.java)
                .eq(MyCollect::userId, id())
                .eq(MyCollect::mode, 2)
                .eq(MyCollect::type, myCollectDto.type)
                .orderByDesc(MyCollect::createTime)
        )
            .let { page ->
                Ok(
                    "获取我的收藏记录成功", Page<MyRecordVo>()
                        .apply {
                            BeanUtil.copyProperties(page, this)
                            records = page.records.takeIf { it.isNotEmpty() }
                                ?.run {
                                    mapNotNull {
                                        myRecordService.getById(it.recordId)?.run { trans(this) }
                                    }
                                }
                        }.pageInfo()
                )

            }

    override fun collectRecord(id: Int): Data<Boolean> =
        myCollectService.getOne(
            KtQueryWrapper(MyCollect::class.java)
                .eq(MyCollect::mode, 2)
                .eq(MyCollect::recordId, id)
                .eq(MyCollect::userId, id())
        )
            ?.run {
                myRecordService.updateById(myRecordService.getById(id).apply {
                    collectNum = collectNum?.minus(1)
                })
                Ok("取消收藏成功", myCollectService.removeById(this.id))
            }
            ?: run {
                myRecordService.updateById(myRecordService.getById(id).apply {
                    collectNum = collectNum?.plus(1)
                })
                Ok("收藏成功", myCollectService.save(MyCollect().apply {
                    mode = 2
                    recordId = id
                    userId = id()
                    type = myRecordService.getById(id)?.type
                }))
            }

    override fun likeRecord(id: Int): Data<Boolean> =
        myCollectService.getOne(
            KtQueryWrapper(MyCollect::class.java)
                .eq(MyCollect::mode, 1)
                .eq(MyCollect::recordId, id)
                .eq(MyCollect::userId, id())
        )
            ?.run {
                myCollectService.removeById(this.id)
                myRecordService.updateById(myRecordService.getById(id).apply {
                    likeNum = likeNum?.minus(1)
                })
                Ok("取消点赞成功", true)
            }
            ?: run {

                myCollectService.save(MyCollect().apply {
                    mode = 1
                    recordId = id
                    userId = id()
                    type = myRecordService.getById(id)?.type
                })
                myRecordService.updateById(myRecordService.getById(id).apply {
                    likeNum = likeNum?.plus(1)
                })

                Ok("点赞成功", true)
            }

    override fun reportRecord(recordReportDto: TemplateReportService.RecordReportDto): Data<Boolean> =
        templateReportService.saveTemplateReport(recordReportDto)

    override fun likeNum(id: Int): Long =
        myCollectService.count(
            KtQueryWrapper(MyCollect::class.java)
                .eq(MyCollect::mode, 1)
                .eq(MyCollect::recordId, id)
        )
            .toLong()

    override fun recommendMyRecord(id: Int): Data<Boolean> =
        recommendService.getOne(
            KtQueryWrapper(Recommend::class.java)
                .eq(Recommend::userId, id())
                .eq(Recommend::recordId, id)
                .last("limit 1")
        )
            ?.run {
                Fail("不可重复推荐!")
            } ?: kotlin.run {
            Ok("推荐成功", recommendService.save(Recommend().apply {
                appUserService.getById(id())?.let {
                    userId = it.id
                    username = it.username
                    userInfoId = it.userId
                }
                myRecordService.getById(id)?.let {
                    recordId = it.id?.toInt()
                    BeanUtil.copyProperties(it, this)
                }
            }))
        }

    override fun removeMyRecord(ids: String): Data<Boolean> =
        Ok("批量移除成功", run {

            ids.split(",").forEach {
                myRecordService.remove(
                    KtQueryWrapper(MyRecord::class.java)
                        .eq(MyRecord::pid, it)
                )
                myRecordService.removeById(it)
            }
            true
        })
}